/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.handlers.commands.call;

import com.hercules.android.receiver.AndroidReceiver;
import com.hercules.android.receiver.handlers.commands.AuxiliaryXRECommandHandler;
import com.hercules.android.receiver.handlers.commands.XRECommandHandlerException;
import com.hercules.android.receiver.handlers.commands.XREObjectCommandHandler;
import com.hercules.android.receiver.model.commands.XRECallCommand;
import com.hercules.android.receiver.model.commands.XRECommand;
import com.hercules.android.receiver.model.objects.XREObject;

import java.util.Map;
import java.util.Map.Entry;

/**
 * Represents the call command handler that will handle the call commands. It extends from XREObjectCommandHandler. It
 * will delegate to more specialized command handlers.
 * <p>
 * <b>Sample Usage:</b>
 *
 * <pre>
 * // create XRECallCommandHandler instance
 * XRECallCommandHandler handler = new XRECallCommandHandler();
 *
 * Map&lt;String, XREObjectCommandHandler&gt; handlers = new HashMap&lt;String, XREObjectCommandHandler&gt;();
 * handlers.put(&quot;XREView&quot;, new MockGenerateHandler());
 *
 * // set required fields
 * handler.setCallCommandHandlers(handlers);
 *
 * // prepare command and receiver
 * AndroidReceiver receiver = prepareReceiver();
 * XRECallCommand command = prepareCommand(&quot;play&quot;);
 *
 * // process command message
 * handler.processCommandMessage(command, receiver);
 * </pre>
 * <p>
 * <b>Thread Safety:</b> Not thread safe since it is mutable.
 *
 * @author DanLazar, orial
 * @version 1.0
 */
public class XRECallCommandHandler extends XREObjectCommandHandler {
    /**
     * The mapping between object class names and command handlers.
     * <p>
     * For example, If the object to be handled is XRESound then this map should have an entry with "XRESound" key and
     * XRESoundCallCommandHandler objects. The keys cannot be null/empty and he values cannot be null. It is mutable.
     * Has a setter and getter. Initially it is null but after the setter is called it cannot be null or empty. Used in
     * processCommand method.
     */
    private Map<String, XREObjectCommandHandler> callCommandHandlers;

    /**
     * Default empty constructor.
     */
    public XRECallCommandHandler() {
        // empty
    }

    /**
     * Gets the mapping between object class names and command handlers.
     *
     * @return the mapping between object class names and command handlers
     */
    public Map<String, XREObjectCommandHandler> getCallCommandHandlers() {
        return callCommandHandlers;
    }

    /**
     * Sets the mapping between object class names and command handlers.
     *
     * @param callCommandHandlers the mapping between object class names and command handlers
     * @throws IllegalArgumentException if parameter is null or empty or if it has null/empty keys or null values
     */
    public void setCallCommandHandlers(Map<String, XREObjectCommandHandler> callCommandHandlers) {
        // check argument
        Helper.checkNotNull(callCommandHandlers, "callCommandHandlers");
        if (callCommandHandlers.isEmpty()) {
            throw new IllegalArgumentException("Argument 'callCommandHandlers' should not be empty.");
        }
        for (Entry<String, XREObjectCommandHandler> entry : callCommandHandlers.entrySet()) {
            Helper.checkString(entry.getKey(), "callCommandHandlers.key");
            Helper.checkNotNull(entry.getValue(), "callCommandHandlers.value");
        }

        this.callCommandHandlers = callCommandHandlers;
    }

    /**
     * Process the given command by delegating to more specific call command handlers.
     *
     * @param command the command
     * @param receiver the receiver
     * @throws IllegalArgumentException if any parameter is null
     * @throws IllegalStateException if required fields are not set
     * @throws XRECommandHandlerException if any error occurs while processing the command
     */
    @Override
    public void processCommandMessage(XRECommand command, AndroidReceiver receiver) throws XRECommandHandlerException {
        String methodName = "processCommandMessage";
        Helper.logEnter(getLogger(), this, methodName, command, receiver);

        try {
            // check arguments and get target object
            XRECallCommand callCommand = Helper.checkCommand(command);
            XREObject object = Helper.checkReceiver(receiver, callCommand, XREObject.class);

            // check state
            Helper.checkState(callCommandHandlers, "callCommandHandlers");

            // perform pre-painting
            AuxiliaryXRECommandHandler handler = getPrePaintingHandler();
            if (handler != null) {
                handler.processCommandMessage(command, receiver);
            }

            // get handler
            XREObjectCommandHandler cmdHandler = callCommandHandlers.get(object.getClass().getSimpleName());
            if (cmdHandler == null) {
                throw new XRECommandHandlerException("Object '" + object
                    + "' do not have corresponding call command handler.");
            }
            cmdHandler.onCommandMessage(command, receiver);

            Helper.logExit(getLogger(), this, methodName);
        } catch (IllegalArgumentException e) {
            Helper.logError(getLogger(), e, methodName, e);
        } catch (IllegalStateException e) {
            Helper.logError(getLogger(), e, methodName, e);
        } catch (XRECommandHandlerException e) {
            Helper.logError(getLogger(), e, methodName, e);
        }
    }
}